#include "tcpm.h"
#include "tcpci_event.h"
#include "tcpci.h"
#include "tcpci_core.h"
#include "tcpci_timer.h"
#include "tcpm_pd.h"
#include "pd_core.h"
#include "pd_dpm_core.h"
#include "pd_dpm_pdo_select.h"
#include "pd_dpm_prv.h"

#undef LOG_TAG
#define LOG_TAG    "tcpm"
#include "elog.h"

#ifndef maybe_unused
#define maybe_unused    __attribute__((unused))
#endif

/* Check status */
static int tcpm_check_typec_attached(struct sgm41620_tcpc *tcpc)
{
    if (tcpc->typec_attach_old == TYPEC_UNATTACHED ||
        tcpc->typec_attach_new == TYPEC_UNATTACHED)
        return TCPM_ERROR_UNATTACHED;

    return 0;
}

static int tcpm_check_pd_attached(struct sgm41620_tcpc *tcpc)
{
    int ret = TCPM_SUCCESS;
    struct pd_port *pd_port = p_pd_port;
    struct pe_data *pe_data = &pd_port->pe_data;

    if (!tcpc->pd_inited_flag) {
        ret = TCPM_ERROR_PE_NOT_READY;
        goto unlock_typec_out;
    }

    ret = tcpm_check_typec_attached(tcpc);

unlock_typec_out:
    if (ret != TCPM_SUCCESS)
        return ret;

    if (!pe_data->pd_prev_connected) {
        ret = TCPM_ERROR_NO_PD_CONNECTED;
        goto unlock_pd_out;
    }

    if (!pe_data->pe_ready)
        ret = TCPM_ERROR_PE_NOT_READY;

unlock_pd_out:
    return ret;
}

int maybe_unused tcpm_shutdown(struct sgm41620_tcpc *tcpc)
{
#if CONFIG_TCPC_SHUTDOWN_VBUS_DISABLE
    if (tcpc->typec_power_ctrl)
        tcpci_disable_vbus_control(tcpc);
#endif	/* CONFIG_TCPC_SHUTDOWN_VBUS_DISABLE */

    if (tcpc->ops->deinit)
        tcpc->ops->deinit();

    return 0;
}

int maybe_unused tcpm_inquire_remote_cc(struct sgm41620_tcpc *tcpc,
	uint8_t *cc1, uint8_t *cc2, bool real_time)
{
    int rv = 0;

    if (real_time) {
        rv = tcpci_get_cc(tcpc);
        if (rv < 0)
            goto out;
    }

    *cc1 = tcpc->typec_remote_cc[0];
    *cc2 = tcpc->typec_remote_cc[1];
out:
    return rv;
}

int maybe_unused tcpm_inquire_typec_remote_rp_curr(struct sgm41620_tcpc *tcpc)
{
    if (tcpm_check_typec_attached(tcpc))
        return 0;

    switch (tcpc->typec_remote_rp_level) {
    case TYPEC_CC_VOLT_SNK_1_5:
        return 1500;
    case TYPEC_CC_VOLT_SNK_3_0:
        return 3000;
    case TYPEC_CC_VOLT_SNK_DFT:
        return 500;
    default:
        return 0;
    }
}

int maybe_unused tcpm_inquire_vbus_level(struct sgm41620_tcpc *tcpc, bool real_time)
{
    int rv = 0;
    uint16_t power_status = 0;

    if (real_time) {
        rv = tcpci_get_power_status(tcpc, &power_status);
        if (rv < 0)
            return rv;
    }
    rv = tcpc->vbus_level; // enum tcpm_vbus_level

    return rv;
}

bool maybe_unused tcpm_inquire_cc_polarity(struct sgm41620_tcpc *tcpc)
{
    return tcpc->typec_polarity; // enum tcpc_polarity
}

uint8_t maybe_unused tcpm_inquire_typec_attach_state(struct sgm41620_tcpc *tcpc)
{
    return tcpc->typec_attach_new; // enum typec_attach_type
}

uint8_t maybe_unused tcpm_inquire_typec_role(struct sgm41620_tcpc *tcpc)
{
    return tcpc->typec_role; // enum typec_role_defination
}

uint8_t maybe_unused tcpm_inquire_typec_local_rp(struct sgm41620_tcpc *tcpc)
{
    return tcpc->typec_local_rp_level;
}

int maybe_unused tcpm_typec_set_usb_sink_curr(struct sgm41620_tcpc *tcpc, int curr)
{
    bool force_sink_vbus = true;
#if CONFIG_USB_POWER_DELIVERY
    struct pd_port *pd_port = p_pd_port;

    if (pd_port->pe_data.pd_prev_connected)
        force_sink_vbus = false;
#endif

    tcpc->typec_usb_sink_curr = curr;

    if (tcpc->typec_remote_rp_level != TYPEC_CC_VOLT_SNK_DFT)
        force_sink_vbus = false;

    if (force_sink_vbus) {
        tcpci_sink_vbus(tcpc, TCP_VBUS_CTRL_TYPEC, TCPC_VBUS_SINK_5V, -1);
    }

    return 0;
}

int maybe_unused tcpm_typec_set_rp_level(struct sgm41620_tcpc *tcpc, uint8_t level)
{
    int ret = 0;

    ret = tcpc_typec_set_rp_level(tcpc, level);

    return ret;
}

int maybe_unused tcpm_typec_error_recovery(struct sgm41620_tcpc *tcpc)
{
    return tcpc_typec_error_recovery(tcpc);
}

int maybe_unused tcpm_typec_disable_function(struct sgm41620_tcpc *tcpc, bool disable)
{
    return (disable ? tcpc_typec_disable : tcpc_typec_enable)(tcpc);
}

#if CONFIG_USB_POWER_DELIVERY
bool maybe_unused tcpm_inquire_pd_connected(struct sgm41620_tcpc *tcpc)
{
    return p_pd_port->pe_data.pd_connected;
}

bool maybe_unused tcpm_inquire_pd_prev_connected(struct sgm41620_tcpc *tcpc)
{
    return p_pd_port->pe_data.pd_prev_connected;
}

uint8_t maybe_unused tcpm_inquire_pd_data_role(struct sgm41620_tcpc *tcpc)
{
    return p_pd_port->data_role;
}

uint8_t maybe_unused tcpm_inquire_pd_power_role(struct sgm41620_tcpc *tcpc)
{
    return p_pd_port->power_role;
}

uint8_t tcpm_inquire_pd_vconn_role(struct sgm41620_tcpc *tcpc)
{
    struct pd_port *pd_port = p_pd_port;
    uint8_t vconn_role = pd_port->vconn_role;
#if CONFIG_USB_PD_VCONN_SAFE5V_ONLY
    struct pe_data *pe_data = &pd_port->pe_data;

    if (pe_data->vconn_highv_prot)
        vconn_role = pe_data->vconn_highv_prot_role;
#endif

    return vconn_role;
}

uint8_t tcpm_inquire_pd_pe_ready(struct sgm41620_tcpc *tcpc)
{
    return p_pd_port->pe_data.pe_ready;
}

uint32_t tcpm_inquire_cable_current(struct sgm41620_tcpc *tcpc)
{
    struct pd_port *pd_port = p_pd_port;
    uint32_t ret = 0;

    if (pd_port->pe_data.cable_discovered_state)
        ret = pd_get_cable_current_limit(pd_port);

    return ret;
}


uint32_t tcpm_inquire_dpm_flags(struct sgm41620_tcpc *tcpc)
{
    return p_pd_port->pe_data.dpm_flags;
}

uint32_t tcpm_inquire_dpm_caps(struct sgm41620_tcpc *tcpc)
{
    return p_pd_port->dpm_caps;
}

void tcpm_set_dpm_caps(struct sgm41620_tcpc *tcpc, uint32_t caps)
{
    p_pd_port->dpm_caps = caps;
}

int tcpm_inquire_pd_contract(struct sgm41620_tcpc *tcpc, int *mv, int *ma)
{
    int ret;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS)
        return ret;

    if ((mv == NULL) || (ma == NULL))
        return TCPM_ERROR_PARAMETER;

    if (p_pd_port->pe_data.explicit_contract) {
        *mv = p_pd_port->request_v;
        *ma = p_pd_port->request_i;
    } else {
        ret = TCPM_ERROR_NO_EXPLICIT_CONTRACT;
    }

    return ret;
}

uint8_t tcpm_get_pd_connect_state(struct sgm41620_tcpc *tcpc)
{
    if (p_pd_port->power_role == PD_ROLE_SOURCE) {
        if (pd_check_rev30(p_pd_port))
            return PD_CONNECT_PE_READY_SRC_PD30;

        return PD_CONNECT_PE_READY_SRC;
    }

    if (pd_check_rev30(p_pd_port)) {
        if (pd_is_source_support_apdo(p_pd_port))
            return PD_CONNECT_PE_READY_SNK_APDO;

        return PD_CONNECT_PE_READY_SNK_PD30;
    }

    return PD_CONNECT_PE_READY_SNK;
}

int tcpm_inquire_cable_inform(struct sgm41620_tcpc *tcpc, uint32_t *vdos)
{
    int ret;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS)
        return ret;

    if (vdos == NULL)
        return TCPM_ERROR_PARAMETER;

    if (p_pd_port->pe_data.cable_discovered_state) {
        memcpy(vdos, p_pd_port->pe_data.cable_vdos, sizeof(uint32_t) * VDO_MAX_NR);
    } else {
        ret = TCPM_ERROR_NO_POWER_CABLE;
    }

    return ret;
}

int tcpm_inquire_pd_partner_inform(struct sgm41620_tcpc *tcpc, uint32_t *vdos)
{
#if CONFIG_USB_PD_KEEP_PARTNER_ID
    int ret;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS)
        return ret;

    if (vdos == NULL)
        return TCPM_ERROR_PARAMETER;

    if (p_pd_port->pe_data.partner_id_present) {
        memcpy(vdos, p_pd_port->pe_data.partner_vdos, sizeof(uint32_t) * VDO_MAX_NR);
    } else {
        ret = TCPM_ERROR_NO_PARTNER_INFORM;
    }

    return ret;
#else
    return TCPM_ERROR_NO_SUPPORT;
#endif
}

int tcpm_inquire_pd_partner_svids(
	struct sgm41620_tcpc *tcpc, struct tcpm_svid_list *list)
{
#if CONFIG_USB_PD_KEEP_SVIDS
    int ret;
    struct svdm_svid_list *svdm_list = &p_pd_port->pe_data.remote_svid_list;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS)
        return ret;

    if (list == NULL)
        return TCPM_ERROR_PARAMETER;

    if (svdm_list->cnt) {
        list->cnt = svdm_list->cnt;
        memcpy(list->svids, svdm_list->svids, sizeof(uint16_t) * svdm_list->cnt);
    } else {
        ret = TCPM_ERROR_NO_PARTNER_INFORM;
    }

    return ret;
#else
    return TCPM_ERROR_NO_SUPPORT;
#endif
}

int tcpm_inquire_pd_partner_modes(
	struct sgm41620_tcpc *tcpc, uint16_t svid, struct tcpm_mode_list *list)
{
    int ret = TCPM_SUCCESS;
    struct svdm_svid_data *svid_data = NULL;

    svid_data = dpm_get_svdm_svid_data(p_pd_port, USB_SID_DISPLAYPORT);

    if (svid_data == NULL) {
        return TCPM_ERROR_PARAMETER;
    }

    if (svid_data->remote_mode.mode_cnt) {
        list->cnt = svid_data->remote_mode.mode_cnt;
        memcpy(list->modes, svid_data->remote_mode.mode_vdo, sizeof(uint32_t) * list->cnt);
    } else {
        ret = TCPM_ERROR_NO_PARTNER_INFORM;
    }

    return ret;
}

int tcpm_inquire_pd_source_cap(struct sgm41620_tcpc *tcpc, struct tcpm_power_cap *cap)
{
    int ret;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS)
        return ret;

    if (cap == NULL)
        return TCPM_ERROR_PARAMETER;

    if (p_pd_port->pe_data.remote_src_cap.nr) {
        cap->cnt = p_pd_port->pe_data.remote_src_cap.nr;
        memcpy(cap->pdos, p_pd_port->pe_data.remote_src_cap.pdos, sizeof(uint32_t) * cap->cnt);
    } else {
        ret = TCPM_ERROR_NO_SOURCE_CAP;
    }

    return ret;
}

int tcpm_inquire_pd_sink_cap(struct sgm41620_tcpc *tcpc, struct tcpm_power_cap *cap)
{
    int ret;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS)
        return ret;

    if (cap == NULL)
        return TCPM_ERROR_PARAMETER;

    if (p_pd_port->pe_data.remote_snk_cap.nr) {
        cap->cnt = p_pd_port->pe_data.remote_snk_cap.nr;
        memcpy(cap->pdos, p_pd_port->pe_data.remote_snk_cap.pdos, sizeof(uint32_t) * cap->cnt);
    } else {
        ret = TCPM_ERROR_NO_SINK_CAP;
    }

    return ret;
}

static bool tcpm_extract_power_cap_val(uint32_t pdo, struct tcpm_power_cap_val *cap)
{
    struct dpm_pdo_info_t info;

    dpm_extract_pdo_info(pdo, &info);

    cap->type = info.type;
    cap->min_mv = info.vmin;
    cap->max_mv = info.vmax;

    if (info.type == DPM_PDO_TYPE_BAT)
        cap->uw = info.uw;
    else
        cap->ma = info.ma;

#if CONFIG_USB_PD_REV30
    if (info.type == DPM_PDO_TYPE_APDO) {
        cap->apdo_type = info.apdo_type;
        cap->pwr_limit = info.pwr_limit;
    }
#endif

    return cap->type != TCPM_POWER_CAP_VAL_TYPE_UNKNOWN;
}

int tcpm_get_remote_power_cap(struct sgm41620_tcpc *tcpc,
		struct tcpm_remote_power_cap *remote_cap)
{
    struct tcpm_power_cap_val cap;
    int i;

    remote_cap->selected_cap_idx = p_pd_port->pe_data.selected_cap;
    remote_cap->nr = p_pd_port->pe_data.remote_src_cap.nr;
    for (i = 0; i < remote_cap->nr; i++) {
        tcpm_extract_power_cap_val(p_pd_port->pe_data.remote_src_cap.pdos[i], &cap);
        remote_cap->max_mv[i] = cap.max_mv;
        remote_cap->min_mv[i] = cap.min_mv;
        if (cap.type == TCPM_POWER_CAP_VAL_TYPE_BATTERY)
            remote_cap->ma[i] = cap.uw / cap.min_mv;
        else
            remote_cap->ma[i] = cap.ma;
        remote_cap->type[i] = cap.type;
    }

    return TCPM_SUCCESS;
}

static inline int __tcpm_inquire_select_source_cap(
	struct pd_port *pd_port, struct tcpm_power_cap_val *cap_val)
{
    uint8_t sel;
    struct pe_data *pe_data = &pd_port->pe_data;

    if (pd_port->power_role != PD_ROLE_SINK)
        return TCPM_ERROR_POWER_ROLE;

    sel = RDO_POS(pd_port->last_rdo) - 1;
    if (sel >= pe_data->remote_src_cap.nr)
        return TCPM_ERROR_NO_SOURCE_CAP;

    if (!tcpm_extract_power_cap_val(pe_data->remote_src_cap.pdos[sel], cap_val))
        return TCPM_ERROR_NOT_FOUND;

    return TCPM_SUCCESS;
}

int tcpm_inquire_select_source_cap(
		struct sgm41620_tcpc *tcpc, struct tcpm_power_cap_val *cap_val)
{
    int ret;

    if (cap_val == NULL)
        return TCPM_ERROR_PARAMETER;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS)
        return ret;

    ret = __tcpm_inquire_select_source_cap(p_pd_port, cap_val);

    return ret;
}

int tcpm_inquire_pd_local_source_cap(
	struct sgm41620_tcpc *tcpc, struct tcpm_power_cap *cap)
{
    if (cap == NULL)
        return TCPM_ERROR_PARAMETER;

    cap->cnt = p_pd_port->local_src_cap_default.nr;
    memcpy(cap->pdos, p_pd_port->local_src_cap_default.pdos, sizeof(uint32_t) * cap->cnt);

    return TCPM_SUCCESS;
}

int tcpm_set_pd_local_source_cap(
	struct sgm41620_tcpc *tcpc, struct tcpm_power_cap *cap)
{
    struct tcpm_power_cap_val cap_val;

    if (cap == NULL)
        return TCPM_ERROR_PARAMETER;
    else if (!tcpm_extract_power_cap_val(cap->pdos[0], &cap_val))
        return TCPM_ERROR_PARAMETER;
    else if (cap->cnt == 0 || cap->cnt > 7 ||
             cap_val.type != TCPM_POWER_CAP_VAL_TYPE_FIXED ||
             cap_val.max_mv != 5000 || cap_val.min_mv != 5000)
        return TCPM_ERROR_PARAMETER;

    p_pd_port->local_src_cap_default.nr = cap->cnt;
    memcpy(p_pd_port->local_src_cap_default.pdos, cap->pdos, sizeof(uint32_t) * cap->cnt);

    return TCPM_SUCCESS;
}

bool tcpm_inquire_usb_comm(struct sgm41620_tcpc *tcpc)
{
    return !!(p_pd_port->pe_data.dpm_flags & DPM_FLAGS_PARTNER_USB_COMM);
}

/* Request TCPC to send PD Request */
#define TCPM_BK_PD_CMD_TOUT         500
/* tPSTransition 550 ms */
#define TCPM_BK_REQUEST_TOUT        1500
/* tPSSourceOff 920 ms, tPSSourceOn 480 ms*/
#define TCPM_BK_PR_SWAP_TOUT        2500
#define TCPM_BK_HARD_RESET_TOUT     3500

static int tcpm_put_tcp_dpm_event(
	struct sgm41620_tcpc *tcpc, struct tcp_dpm_event *event);

#if CONFIG_USB_PD_BLOCK_TCPM
static int tcpm_put_tcp_dpm_event_bk(
	struct sgm41620_tcpc *tcpc, struct tcp_dpm_event *event,
	uint32_t tout_ms, uint8_t *data, uint8_t size);
#endif	/* CONFIG_USB_PD_BLOCK_TCPM */

static inline int tcpm_put_tcp_dpm_event_cb(struct sgm41620_tcpc *tcpc,
    struct tcp_dpm_event *event,
    const struct tcp_dpm_event_cb_data *cb_data)
{
    if (cb_data) {
        event->user_data = cb_data->user_data;
        event->event_cb = cb_data->event_cb;
    }
    return tcpm_put_tcp_dpm_event(tcpc, event);
}

static int tcpm_put_tcp_dpm_event_cbk1(struct sgm41620_tcpc *tcpc,
	struct tcp_dpm_event *event,
	const struct tcp_dpm_event_cb_data *cb_data, uint32_t tout_ms)
{
#if CONFIG_USB_PD_BLOCK_TCPM
    if (cb_data == NULL) {
        return tcpm_put_tcp_dpm_event_bk(
            tcpc, event, tout_ms, NULL, 0);
    }
#endif

    return tcpm_put_tcp_dpm_event_cb(tcpc, event, cb_data);
}

int tcpm_dpm_pd_power_swap(struct sgm41620_tcpc *tcpc,
	uint8_t role, const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_PR_SWAP_AS_SNK + role,
    };

    if (role > 1)
        return TCPM_ERROR_PARAMETER;

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PR_SWAP_TOUT);
}

int tcpm_dpm_pd_data_swap(struct sgm41620_tcpc *tcpc,
	uint8_t role, const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_DR_SWAP_AS_UFP + role,
    };

    if (role > 1)
        return TCPM_ERROR_PARAMETER;

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}

int tcpm_dpm_pd_vconn_swap(struct sgm41620_tcpc *tcpc,
	uint8_t role, const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_VCONN_SWAP_OFF + role,
    };

    if (role > 1)
        return TCPM_ERROR_PARAMETER;

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}

int tcpm_dpm_pd_soft_reset(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_SOFTRESET,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT);
}

int tcpm_dpm_pd_get_source_cap(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_SOURCE_CAP,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT);
}

int tcpm_dpm_pd_get_sink_cap(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_SINK_CAP,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}

int tcpm_dpm_pd_source_cap(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_SOURCE_CAP,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT);
}

int tcpm_dpm_pd_request(struct sgm41620_tcpc *tcpc,
	int mv, int ma, const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_REQUEST,
        .tcp_dpm_data.pd_req.mv = mv,
        .tcp_dpm_data.pd_req.ma = ma,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT);
}

int tcpm_dpm_pd_request_ex(struct sgm41620_tcpc *tcpc,
	uint8_t pos, int vmin, uint32_t max, uint32_t oper,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_REQUEST_EX,
        .tcp_dpm_data.pd_req_ex.pos = pos,
        .tcp_dpm_data.pd_req_ex.vmin = vmin,
        .tcp_dpm_data.pd_req_ex.max = max,
        .tcp_dpm_data.pd_req_ex.oper = oper,
    };

    if (oper > max)
        return TCPM_ERROR_PARAMETER;

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT);
}

int tcpm_dpm_pd_bist_cm2(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_BIST_CM2,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}

#if CONFIG_USB_PD_REV30
static int tcpm_put_tcp_dpm_event_cbk2(struct sgm41620_tcpc *tcpc,
	struct tcp_dpm_event *event,
	const struct tcp_dpm_event_cb_data *cb_data,
	uint32_t tout_ms, uint8_t *data, uint8_t size)
{
#if CONFIG_USB_PD_BLOCK_TCPM
    if (cb_data == NULL) {
        return tcpm_put_tcp_dpm_event_bk(
            tcpc, event, tout_ms, data, size);
    }
#endif

    return tcpm_put_tcp_dpm_event_cb(tcpc, event, cb_data);
}

int tcpm_dpm_pd_get_source_cap_ext(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data,
	struct pd_source_cap_ext *src_cap_ext)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_SOURCE_CAP_EXT,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) src_cap_ext, PD_SCEDB_SIZE);
}

int tcpm_dpm_pd_get_sink_cap_ext(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data,
	struct pd_sink_cap_ext *snk_cap_ext)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_SINK_CAP_EXT,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) snk_cap_ext, PD_SKEDB_SIZE);
}

int tcpm_dpm_pd_get_status(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data, struct pd_status *status)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_STATUS,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) status, PD_SDB_SIZE);
}


static int tcpm_dpm_pd_get_pps_status_raw(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data,
	struct pd_pps_status_raw *pps_status)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_PPS_STATUS,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) pps_status, PD_PPSSDB_SIZE);
}

int tcpm_dpm_pd_get_pps_status(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data,
	struct pd_pps_status *pps_status)
{
    int ret;
    struct pd_pps_status_raw pps_status_raw;

    ret = tcpm_dpm_pd_get_pps_status_raw(
          tcpc, cb_data, &pps_status_raw);

    if (ret != 0)
        return ret;

    if (pps_status_raw.output_vol_raw == 0xffff)
        pps_status->output_mv = -1;
    else
        pps_status->output_mv = PD_PPS_GET_OUTPUT_MV(pps_status_raw.output_vol_raw);

    if (pps_status_raw.output_curr_raw == 0xff)
        pps_status->output_ma = -1;
    else
        pps_status->output_ma = PD_PPS_GET_OUTPUT_MA(pps_status_raw.output_curr_raw);

    pps_status->real_time_flags = pps_status_raw.real_time_flags;
    return ret;
}


int tcpm_dpm_pd_get_country_code(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data,
	struct pd_country_codes *ccdb)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_COUNTRY_CODE,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) ccdb, PD_CCDB_MAX_SIZE);
}


int tcpm_dpm_pd_get_country_info(struct sgm41620_tcpc *tcpc, uint32_t ccdo,
	const struct tcp_dpm_event_cb_data *cb_data,
	struct pd_country_info *cidb)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_COUNTRY_INFO,
        .tcp_dpm_data.index = ccdo,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) cidb, PD_CIDB_MAX_SIZE);
}


int tcpm_dpm_pd_get_bat_cap(struct sgm41620_tcpc *tcpc,
	struct pd_get_battery_capabilities *gbcdb,
	const struct tcp_dpm_event_cb_data *cb_data,
	struct pd_battery_capabilities *bcdb)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_BAT_CAP,
        .tcp_dpm_data.gbcdb = *gbcdb,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) bcdb, PD_BCDB_SIZE);
}


int tcpm_dpm_pd_get_bat_status(struct sgm41620_tcpc *tcpc,
	struct pd_get_battery_status *gbsdb,
	const struct tcp_dpm_event_cb_data *cb_data,
	uint32_t *bsdo)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_BAT_STATUS,
        .tcp_dpm_data.gbsdb = *gbsdb,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) bsdo, sizeof(uint32_t) * PD_BSDO_SIZE);
}


int tcpm_dpm_pd_get_mfrs_info(struct sgm41620_tcpc *tcpc,
	struct pd_get_manufacturer_info *gmidb,
	const struct tcp_dpm_event_cb_data *cb_data,
	struct pd_manufacturer_info *midb)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_MFRS_INFO,
        .tcp_dpm_data.gmidb = *gmidb,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) midb, PD_MIDB_MAX_SIZE);
}


int tcpm_dpm_pd_get_revision(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data,
	uint32_t *rmdo)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_GET_REVISION,
    };

    return tcpm_put_tcp_dpm_event_cbk2(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT,
        (uint8_t *) rmdo, sizeof(uint32_t) * PD_RMDO_SIZE);
}


int tcpm_dpm_pd_alert(struct sgm41620_tcpc *tcpc,
	uint32_t ado, const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_ALERT,
        .tcp_dpm_data.index = ado,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}
#endif

int tcpm_dpm_pd_hard_reset(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_HARD_RESET,
    };

    /* check not block case !!! */
    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_HARD_RESET_TOUT);
}


int tcpm_dpm_pd_error_recovery(struct sgm41620_tcpc *tcpc)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_ERROR_RECOVERY,
    };

    if (tcpm_put_tcp_dpm_event(tcpc, &tcp_event) != TCPM_SUCCESS)
        tcpm_typec_error_recovery(tcpc);

    return TCPM_SUCCESS;
}

int tcpm_dpm_pd_cable_soft_reset(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_CABLE_SOFTRESET,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}

int tcpm_dpm_vdm_discover_cable_id(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_DISCOVER_CABLE_ID,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}

int tcpm_dpm_vdm_discover_id(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_DISCOVER_ID,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}


int tcpm_dpm_vdm_discover_svids(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_DISCOVER_SVIDS,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}

int tcpm_dpm_vdm_discover_modes(struct sgm41620_tcpc *tcpc,
	uint16_t svid, const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_DISCOVER_MODES,
        .tcp_dpm_data.svdm_data.svid = svid,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}


int tcpm_dpm_vdm_enter_mode(struct sgm41620_tcpc *tcpc,
	uint16_t svid, uint8_t ops,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_ENTER_MODE,
        .tcp_dpm_data.svdm_data.svid = svid,
        .tcp_dpm_data.svdm_data.ops = ops,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}


int tcpm_dpm_vdm_exit_mode(struct sgm41620_tcpc *tcpc,
	uint16_t svid, uint8_t ops,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_EXIT_MODE,
        .tcp_dpm_data.svdm_data.svid = svid,
        .tcp_dpm_data.svdm_data.ops = ops,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}


int tcpm_dpm_vdm_attention(struct sgm41620_tcpc *tcpc,
	uint16_t svid, uint8_t ops,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_ATTENTION,
        .tcp_dpm_data.svdm_data.svid = svid,
        .tcp_dpm_data.svdm_data.ops = ops,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}


int tcpm_inquire_dp_ufp_u_state(struct sgm41620_tcpc *tcpc, uint8_t *state)
{
    int ret;
    struct pd_port *pd_port = p_pd_port;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS)
        return ret;

    if (state == NULL)
        return TCPM_ERROR_PARAMETER;

    *state = pd_get_dp_data(pd_port)->ufp_u_state;

    return ret;
}


int tcpm_dpm_dp_attention(struct sgm41620_tcpc *tcpc,
	uint32_t dp_status, uint32_t mask,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_DP_ATTENTION,
        .tcp_dpm_data.dp_data.val = dp_status,
        .tcp_dpm_data.dp_data.mask = mask,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}


int tcpm_inquire_dp_dfp_u_state(struct sgm41620_tcpc *tcpc, uint8_t *state)
{
    int ret;
    struct pd_port *pd_port = p_pd_port;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS)
        return ret;

    if (state == NULL)
        return TCPM_ERROR_PARAMETER;

    *state = pd_get_dp_data(pd_port)->dfp_u_state;

    return ret;
}

int tcpm_dpm_dp_status_update(struct sgm41620_tcpc *tcpc,
	uint32_t dp_status, uint32_t mask,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_DP_STATUS_UPDATE,
        .tcp_dpm_data.dp_data.val = dp_status,
        .tcp_dpm_data.dp_data.mask = mask,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}

int tcpm_dpm_dp_config(struct sgm41620_tcpc *tcpc,
	uint32_t dp_config, uint32_t mask,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_DP_CONFIG,
        .tcp_dpm_data.dp_data.val = dp_config,
        .tcp_dpm_data.dp_data.mask = mask,
    };

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_PD_CMD_TOUT);
}


int tcpm_dpm_send_custom_vdm(
	struct sgm41620_tcpc *tcpc,
	struct tcp_dpm_custom_vdm_data *cvdm_data,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    int ret;
    struct pd_port *pd_port = p_pd_port;
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_CVDM,
    };

    if (cvdm_data->cnt > PD_DATA_OBJ_SIZE)
        return TCPM_ERROR_PARAMETER;

    memcpy(&tcp_event.tcp_dpm_data.cvdm_data, cvdm_data, sizeof(*cvdm_data));

    ret = tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT);

#if CONFIG_USB_PD_TCPM_CB_2ND
    if (cb_data == NULL && ret == TCP_DPM_RET_SUCCESS && cvdm_data->wait_resp) {
        cvdm_data->cnt = pd_port->cvdm_cnt;
        memcpy(cvdm_data->data, pd_port->cvdm_data,
               sizeof(cvdm_data->data[0]) * cvdm_data->cnt);
    }
#endif

    return ret;
}

#if CONFIG_USB_PD_TCPM_CB_2ND
static void tcpm_replace_curr_tcp_event(
	struct pd_port *pd_port, struct tcp_dpm_event *event)
{
    int reason = TCP_DPM_RET_DENIED_UNKNOWN;

    switch (event->event_id) {
    case TCP_DPM_EVT_HARD_RESET:
        reason = TCP_DPM_RET_DROP_SENT_HRESET;
        break;
    case TCP_DPM_EVT_ERROR_RECOVERY:
        reason = TCP_DPM_RET_DROP_ERROR_REOCVERY;
        break;
    }

    pd_notify_tcp_event_2nd_result(pd_port, reason);
    pd_port->tcp_event_drop_reset_once = true;
    pd_port->tcp_event_id_2nd = event->event_id;
    memcpy(&pd_port->tcp_event, event, sizeof(struct tcp_dpm_event));
}
#endif

static int tcpm_put_tcp_dpm_event(
	struct sgm41620_tcpc *tcpc, struct tcp_dpm_event *event)
{
    int ret;
    bool imme = event->event_id >= TCP_DPM_EVT_IMMEDIATELY;

    ret = tcpm_check_pd_attached(tcpc);
    if (ret != TCPM_SUCCESS && (imme && ret == TCPM_ERROR_UNATTACHED))
        return ret;

    if (imme) {
        ret = pd_put_tcp_pd_event(p_pd_port, event->event_id,
                                  PD_TCP_FROM_TCPM);
#if CONFIG_USB_PD_TCPM_CB_2ND
        if (ret)
            tcpm_replace_curr_tcp_event(p_pd_port, event);
#endif
    } else {
        ret = pd_put_deferred_tcp_event(tcpc, event);
    }

    if (!ret)
        return TCPM_ERROR_PUT_EVENT;

    return TCPM_SUCCESS;
}

uint8_t tcpm_inquire_pd_charging_policy(struct sgm41620_tcpc *tcpc)
{
    return p_pd_port->dpm_charging_policy;
}

uint8_t tcpm_inquire_pd_charging_policy_default(struct sgm41620_tcpc *tcpc)
{
    return p_pd_port->dpm_charging_policy_default;
}

int tcpm_set_pd_charging_policy_default(
    struct sgm41620_tcpc *tcpc, uint8_t policy)
{
    /* PPS should not be default charging policy ... */
    if ((policy & DPM_CHARGING_POLICY_MASK) >= DPM_CHARGING_POLICY_PPS)
        return TCPM_ERROR_PARAMETER;

    p_pd_port->dpm_charging_policy_default = policy;

    return TCPM_SUCCESS;
}

int tcpm_set_pd_charging_policy(struct sgm41620_tcpc *tcpc,
	uint8_t policy, const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_REQUEST_AGAIN,
    };

    struct pd_port *pd_port = p_pd_port;

    /* PPS should call another function ... */
    if ((policy & DPM_CHARGING_POLICY_MASK) >= DPM_CHARGING_POLICY_PPS)
        return TCPM_ERROR_PARAMETER;

    if (pd_port->dpm_charging_policy == policy) {
        return TCPM_SUCCESS;
    }

    pd_port->dpm_charging_policy = policy;

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT);
}

int tcpm_reset_pd_charging_policy(struct sgm41620_tcpc *tcpc,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    return tcpm_set_pd_charging_policy(
        tcpc, p_pd_port->dpm_charging_policy_default, cb_data);
}

#if CONFIG_USB_PD_DIRECT_CHARGE
int tcpm_set_direct_charge_en(struct sgm41620_tcpc *tcpc, bool en)
{
    mutex_lock(&tcpc->access_lock);
    tcpc->pd_during_direct_charge = en;
    mutex_unlock(&tcpc->access_lock);

    return 0;
}

bool tcpm_inquire_during_direct_charge(struct sgm41620_tcpc *tcpc)
{
    return tcpc->pd_during_direct_charge;
}
#endif

#if CONFIG_TYPEC_SNK_DETACH_IGNORE_VBUS
int tcpm_set_exit_attached_snk_via_cc(struct sgm41620_tcpc *tcpc, bool en)
{
    tcpc->pd_exit_attached_snk_via_cc = en;

    return 0;
}

bool tcpm_inquire_exit_attached_snk_via_cc(struct sgm41620_tcpc *tcpc)
{
    return tcpc->pd_exit_attached_snk_via_cc;
}
#endif

static int tcpm_put_tcp_dummy_event(struct sgm41620_tcpc *tcpc)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_DUMMY,
    };

    return tcpm_put_tcp_dpm_event(tcpc, &tcp_event);
}

int tcpm_dpm_set_vconn_supply_mode(struct sgm41620_tcpc *tcpc, uint8_t mode)
{
    struct pd_port *pd_port = p_pd_port;

    tcpc->tcpc_vconn_supply = mode;
    dpm_reaction_set(pd_port,
        DPM_REACTION_DYNAMIC_VCONN |
        DPM_REACTION_VCONN_STABLE_DELAY);

    return tcpm_put_tcp_dummy_event(tcpc);
}

#if CONFIG_USB_PD_REV30_PPS_SINK
int tcpm_set_apdo_charging_policy(struct sgm41620_tcpc *tcpc,
	uint8_t policy, int mv, int ma,
	const struct tcp_dpm_event_cb_data *cb_data)
{
    struct tcp_dpm_event tcp_event = {
        .event_id = TCP_DPM_EVT_REQUEST_AGAIN,
    };

    /* Not PPS should call another function ... */
    if ((policy & DPM_CHARGING_POLICY_MASK) < DPM_CHARGING_POLICY_PPS)
        return TCPM_ERROR_PARAMETER;

    if (!pd_is_source_support_apdo(p_pd_port)) {
//        mutex_unlock(&p_pd_port->pd_lock);
        return TCPM_ERROR_INVALID_POLICY;
    }

    if (p_pd_port->dpm_charging_policy == policy) {
//        mutex_unlock(&p_pd_port->pd_lock);
        return tcpm_dpm_pd_request(tcpc, mv, ma, NULL);
    }

    p_pd_port->dpm_charging_policy = policy;
    p_pd_port->request_v_apdo = mv;
    p_pd_port->request_i_apdo = ma;

    return tcpm_put_tcp_dpm_event_cbk1(
        tcpc, &tcp_event, cb_data, TCPM_BK_REQUEST_TOUT);
}

int tcpm_inquire_pd_source_apdo(struct sgm41620_tcpc *tcpc,
	uint8_t apdo_type, uint8_t *cap_i, struct tcpm_power_cap_val *cap_val)
{
    int ret;
    uint8_t i;
        struct tcpm_power_cap cap;

    ret = tcpm_inquire_pd_source_cap(tcpc, &cap);
    if (ret != TCPM_SUCCESS)
        return ret;

    for (i = *cap_i; i < cap.cnt; i++) {
        if (!tcpm_extract_power_cap_val(cap.pdos[i], cap_val))
            continue;

        if (cap_val->type != TCPM_POWER_CAP_VAL_TYPE_AUGMENT)
            continue;

        if ((cap_val->apdo_type & TCPM_APDO_TYPE_MASK) != apdo_type)
            continue;

        *cap_i = i+1;
        return TCPM_SUCCESS;
    }

    return TCPM_ERROR_NOT_FOUND;
}


bool tcpm_inquire_during_pps_charge(struct sgm41620_tcpc *tcpc)
{
    int ret;
    struct tcpm_power_cap_val cap = {0};

    ret = tcpm_inquire_select_source_cap(tcpc, &cap);
    if (ret != 0)
        return false;

    return (cap.type == TCPM_POWER_CAP_VAL_TYPE_AUGMENT);
}
#endif

#if CONFIG_USB_PD_REV30_BAT_INFO
static void tcpm_alert_bat_changed(
	struct pd_port *pd_port, enum pd_battery_reference ref)
{
#if CONFIG_USB_PD_REV30_ALERT_LOCAL
    uint8_t fixed = 0, swap = 0;

    if (ref >= PD_BAT_REF_SWAP0)
        swap = 1 << (ref - PD_BAT_REF_SWAP0);
    else
        fixed = 1 << (ref - PD_BAT_REF_FIXED0);

    pd_port->pe_data.local_alert |= ADO(ADO_ALERT_BAT_CHANGED, fixed, swap);
#endif
}

static inline uint16_t tcpm_calc_battery_cap(
	struct pd_battery_info *battery_info, uint16_t soc)
{
    uint16_t wh;
    struct pd_battery_capabilities *bat_cap;

    bat_cap = &battery_info->bat_cap;

    if (bat_cap->bat_last_full_cap != BSDO_BAT_CAP_UNKNOWN)
        wh = bat_cap->bat_last_full_cap;
    else if (bat_cap->bat_design_cap != BSDO_BAT_CAP_UNKNOWN)
        wh = bat_cap->bat_design_cap;
    else
        wh = BSDO_BAT_CAP_UNKNOWN;

    if (wh != BSDO_BAT_CAP_UNKNOWN)
        wh = wh * soc / 1000;

    return wh;
}

static int tcpm_update_bsdo(
	struct pd_port *pd_port, enum pd_battery_reference ref,
	uint16_t soc, uint16_t wh, uint8_t status)
{
    uint8_t status_old;
    struct pd_battery_info *battery_info;

    battery_info = pd_get_battery_info(pd_port, ref);
    if (battery_info == NULL)
        return TCPM_ERROR_PARAMETER;

    status_old = BSDO_BAT_INFO(battery_info->bat_status);

    if (soc != 0)
        wh = tcpm_calc_battery_cap(battery_info, soc);

    battery_info->bat_status = BSDO(wh, status);

    if (status_old != status) {
        tcpm_alert_bat_changed(pd_port, ref);
        return TCPM_ALERT;
    }

    return TCPM_SUCCESS;
}

static inline int tcpm_update_bat_status_wh_no_mutex(struct sgm41620_tcpc *tcpc,
	enum pd_battery_reference ref, uint8_t status, uint16_t wh)
{
    return tcpm_update_bsdo(p_pd_port, ref, 0, wh, status);
}

int tcpm_update_bat_status_wh(struct sgm41620_tcpc *tcpc,
	enum pd_battery_reference ref, uint8_t status, uint16_t wh)
{
    int ret;

    ret = tcpm_update_bat_status_wh_no_mutex(tcpc, ref, status, wh);

    if (ret == TCPM_ALERT) {
        ret = TCPM_SUCCESS;
        tcpm_put_tcp_dummy_event(tcpc);
    }

    return ret;
}

static inline int tcpm_update_bat_status_soc_no_mutex(struct sgm41620_tcpc *tcpc,
	enum pd_battery_reference ref, uint8_t status, uint16_t soc)
{
    return tcpm_update_bsdo(p_pd_port, ref, soc, 0, status);
}

int tcpm_update_bat_status_soc(struct sgm41620_tcpc *tcpc,
	enum pd_battery_reference ref, uint8_t status, uint16_t soc)
{
    int ret;

    ret = tcpm_update_bat_status_soc_no_mutex(tcpc, ref, status, soc);

    if (ret == TCPM_ALERT) {
        ret = TCPM_SUCCESS;
        tcpm_put_tcp_dummy_event(tcpc);
    }

    return ret;
}

static inline int tcpm_update_bat_last_full_no_mutex(struct sgm41620_tcpc *tcpc,
	enum pd_battery_reference ref, uint16_t wh)
{
    struct pd_battery_info *battery_info;

    battery_info = pd_get_battery_info(p_pd_port, ref);
    if (battery_info == NULL)
        return TCPM_ERROR_PARAMETER;

    battery_info->bat_cap.bat_last_full_cap = wh;
    return TCPM_SUCCESS;
}

int tcpm_update_bat_last_full(struct sgm41620_tcpc *tcpc,
	enum pd_battery_reference ref, uint16_t wh)
{
    int ret;

    ret = tcpm_update_bat_last_full_no_mutex(tcpc, ref, wh);

    return ret;
}
#endif

#if CONFIG_USB_PD_REV30_STATUS_LOCAL
static void tcpm_alert_status_changed(
	struct sgm41620_tcpc *tcpc, uint8_t ado_type)
{
#if CONFIG_USB_PD_REV30_ALERT_LOCAL
    p_pd_port->pe_data.local_alert |= ADO(ado_type, 0, 0);
#endif
}

int tcpm_update_pd_status_temp(struct sgm41620_tcpc *tcpc,
	enum pd_present_temperature_flag ptf_new, uint8_t temperature)
{
#if CONFIG_USB_PD_REV30_STATUS_LOCAL_TEMP
    uint8_t ado_type = 0;
    enum pd_present_temperature_flag ptf_now;

    ptf_now = (enum pd_present_temperature_flag)PD_STATUS_TEMP_PTF(p_pd_port->pd_status_temp_status);

    if (ptf_now != ptf_new) {
        if ((ptf_new >= PD_PTF_WARNING) || (ptf_now >= PD_PTF_WARNING))
            ado_type |= ADO_ALERT_OPER_CHANGED;

        if (ptf_new == PD_PTF_OVER_TEMP) {
            ado_type |= ADO_ALERT_OTP;
            p_pd_port->pe_data.pd_status_event |= PD_STATUS_EVENT_OTP;
        }

        p_pd_port->pd_status_temp_status = PD_STATUS_TEMP_SET_PTF(ptf_new);
    }

    p_pd_port->pd_status_temp = temperature;
    tcpm_alert_status_changed(tcpc, ado_type);

    if (ado_type)
        tcpm_put_tcp_dummy_event(tcpc);

    return TCPM_SUCCESS;
#else
    return TCPM_ERROR_NO_IMPLEMENT;
#endif
}

int tcpm_update_pd_status_input(
	struct sgm41620_tcpc *tcpc, uint8_t input_new, uint8_t mask)
{
    uint8_t input;
    uint8_t ado_type = 0;

    input = p_pd_port->pd_status_present_in;
    input &= ~mask;
    input |= (input_new & mask);

    if (input != p_pd_port->pd_status_present_in) {
        ado_type = ADO_ALERT_SRC_IN_CHANGED;
        p_pd_port->pd_status_present_in = input;
    }

    tcpm_alert_status_changed(tcpc, ado_type);

    if (ado_type)
        tcpm_put_tcp_dummy_event(tcpc);

    return TCPM_SUCCESS;
}

int tcpm_update_pd_status_bat_input(
	struct sgm41620_tcpc *tcpc, uint8_t bat_input, uint8_t bat_mask)
{
    uint8_t input;
    uint8_t ado_type = 0;
    const uint8_t flag = PD_STATUS_INPUT_INT_POWER_BAT;

    input = p_pd_port->pd_status_bat_in;
    input &= ~bat_mask;
    input |= (bat_input & bat_mask);

    if (input != p_pd_port->pd_status_bat_in) {
        if (input)
            p_pd_port->pd_status_present_in |= flag;
        else
            p_pd_port->pd_status_present_in &= ~flag;

        ado_type = ADO_ALERT_SRC_IN_CHANGED;
    }

    tcpm_alert_status_changed(tcpc, ado_type);

    if (ado_type)
        tcpm_put_tcp_dummy_event(tcpc);

    return TCPM_SUCCESS;
}

int tcpm_update_pd_status_event(struct sgm41620_tcpc *tcpc, uint8_t evt)
{
    uint8_t ado_type = 0;

    if (evt & PD_STATUS_EVENT_OCP)
        ado_type |= ADO_ALERT_OCP;

    if (evt & PD_STATUS_EVENT_OTP)
        ado_type |= ADO_ALERT_OTP;

    if (evt & PD_STATUS_EVENT_OVP)
        ado_type |= ADO_ALERT_OVP;

    evt &= PD_STASUS_EVENT_MASK;

    p_pd_port->pe_data.pd_status_event |= evt;
    tcpm_alert_status_changed(tcpc, ado_type);
    if (ado_type)
        tcpm_put_tcp_dummy_event(tcpc);

    return TCPM_SUCCESS;
}

#endif	/* CONFIG_USB_PD_REV30_STATUS_LOCAL */

#if CONFIG_USB_PD_BLOCK_TCPM
#if TCPM_EVENT_INFO
static const char * const bk_event_ret_name[] = {
	"OK",
	"Unknown",	/* or not support by TCPM */
	"NotReady",
	"LocalCap",
	"PartnerCap",
	"SameRole",
	"InvalidReq",
	"RepeatReq",
	"WrongDR",
	"PDRev",
	"ModalOperation",
#if CONFIG_USB_PD_VCONN_SAFE5V_ONLY
	"VconnHighVProt",
#endif	/* CONFIG_USB_PD_VCONN_SAFE5V_ONLY */

	"Detach",
	"SReset0",
	"SReset1",
	"HReset0",
	"HReset1",
	"Recovery",
	"BIST",
	"PEBusy",
	"Discard",
	"Unexpected",

	"Wait",
	"Reject",
	"TOUT",
	"NAK",
	"NotSupport",

	"BKTOUT",
	"NoResponse",
};
#endif // TCPM_EVENT_INFO

#if CONFIG_USB_PD_TCPM_CB_2ND
static inline void tcpm_dpm_bk_copy_data(struct pd_port *pd_port)
{
	uint8_t size = pd_port->tcpm_bk_cb_data_max;

	if (size >= pd_get_msg_data_size(pd_port))
		size = pd_get_msg_data_size(pd_port);

	if (pd_port->tcpm_bk_cb_data != NULL) {
		memcpy(pd_port->tcpm_bk_cb_data,
			pd_get_msg_data_payload(pd_port), size);
	}
}
#endif	/* CONFIG_USB_PD_TCPM_CB_2ND */

int tcpm_dpm_bk_event_cb(
	struct sgm41620_tcpc *tcpc, int ret, struct tcp_dpm_event *event)
{
	struct pd_port *pd_port = p_pd_port;

	if (pd_port->tcpm_bk_event_id != event->event_id) {
		log_i("bk_event_cb_dummy: expect:%d real:%d\n",
			pd_port->tcpm_bk_event_id, event->event_id);
		return 0;
	}

	pd_port->tcpm_bk_ret = ret;
	pd_port->tcpm_bk_done = true;

#if CONFIG_USB_PD_TCPM_CB_2ND
	if (ret == TCP_DPM_RET_SUCCESS)
		tcpm_dpm_bk_copy_data(pd_port);
#endif	/* CONFIG_USB_PD_TCPM_CB_2ND */

	//wake_up(&pd_port->tcpm_bk_wait_que);
	return 0;
}

static inline int __tcpm_dpm_wait_bk_event(
	struct pd_port *pd_port, uint32_t tout_ms)
{
	int ret = TCP_DPM_RET_BK_TIMEOUT;

	/*wait_event_timeout(pd_port->tcpm_bk_wait_que, pd_port->tcpm_bk_done,
			   msecs_to_jiffies(tout_ms));*/

	if (pd_port->tcpm_bk_done)
		return pd_port->tcpm_bk_ret;

	pd_port->tcpm_bk_event_id = TCP_DPM_EVT_UNKNOWN;

#if CONFIG_USB_PD_TCPM_CB_2ND
	pd_port->tcpm_bk_cb_data = NULL;
#endif	/* CONFIG_USB_PD_TCPM_CB_2ND */

	return ret;
}

static inline int tcpm_dpm_wait_bk_event(
	struct pd_port *pd_port, uint32_t tout_ms)
{
	int ret = __tcpm_dpm_wait_bk_event(pd_port, tout_ms);
#if TCPM_EVENT_INFO
	if (ret < TCP_DPM_RET_NR && ret >= 0)
		log_i("bk_event_cb -> %s\n", bk_event_ret_name[ret]);
#endif // TCPM_EVENT_INFO

	return ret;
}

#if CONFIG_MTK_HANDLE_PPS_TIMEOUT
static void mtk_handle_tcp_event_result(
	struct sgm41620_tcpc *tcpc, struct tcp_dpm_event *event, int ret)
{
	struct tcp_dpm_event evt_hreset = {
		.event_id = TCP_DPM_EVT_HARD_RESET,
	};

	if (ret == TCPM_SUCCESS || ret == TCP_DPM_RET_NOT_SUPPORT)
		return;

	if (event->event_id == TCP_DPM_EVT_GET_STATUS
		|| event->event_id == TCP_DPM_EVT_GET_PPS_STATUS) {
		tcpm_put_tcp_dpm_event(tcpc, &evt_hreset);
	}
}
#endif /* CONFIG_MTK_HANDLE_PPS_TIMEOUT */

static int __tcpm_put_tcp_dpm_event_bk(
	struct sgm41620_tcpc *tcpc, struct tcp_dpm_event *event,
	uint32_t tout_ms, uint8_t *data, uint8_t size)
{
	int ret;
	struct pd_port *pd_port = p_pd_port;

	pd_port->tcpm_bk_done = false;
	pd_port->tcpm_bk_event_id = event->event_id;

#if CONFIG_USB_PD_TCPM_CB_2ND
	pd_port->tcpm_bk_cb_data = data;
	pd_port->tcpm_bk_cb_data_max = size;
#endif	/* CONFIG_USB_PD_TCPM_CB_2ND */

	ret = tcpm_put_tcp_dpm_event(tcpc, event);
	if (ret != TCPM_SUCCESS)
		return ret;

	return tcpm_dpm_wait_bk_event(pd_port, tout_ms);
}

static int tcpm_put_tcp_dpm_event_bk(
	struct sgm41620_tcpc *tcpc, struct tcp_dpm_event *event,
	uint32_t tout_ms, uint8_t *data, uint8_t size)
{
	int ret = TCP_DPM_RET_TIMEOUT, retry = CONFIG_USB_PD_TCPM_CB_RETRY;

	event->event_cb = tcpm_dpm_bk_event_cb;

	do {
		ret = __tcpm_put_tcp_dpm_event_bk(
			tcpc, event, tout_ms, data, size);
	} while ((ret == TCP_DPM_RET_TIMEOUT ||
		  ret == TCP_DPM_RET_DROP_DISCARD ||
		  ret == TCP_DPM_RET_DROP_UNEXPECTED) && (retry-- > 0));


#if !CONFIG_USB_PD_TCPM_CB_2ND
	if ((data != NULL) && (ret == TCPM_SUCCESS))
		return TCPM_ERROR_EXPECT_CB2;
#endif	/* CONFIG_USB_PD_TCPM_CB_2ND */

	if (ret == TCP_DPM_RET_DENIED_REPEAT_REQUEST)
		ret = TCPM_SUCCESS;

#if CONFIG_MTK_HANDLE_PPS_TIMEOUT
	mtk_handle_tcp_event_result(tcpc, event, ret);
#endif /* CONFIG_MTK_HANDLE_PPS_TIMEOUT */

	return ret;
}
#endif

#endif /* CONFIG_USB_POWER_DELIVERY */
